package com.zby.message;

import com.zby.common.exception.BlogException;
import com.zby.common.utils.JwtUtils;
import com.zby.common.utils.ResultEnum;
import io.jsonwebtoken.Claims;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.Instant;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author wuyanzhu
 */
// 使用 @ServerEndpoint 注解表示此类是一个 WebSocket 端点
// 通过 value 注解，指定 websocket 的路径
@Component
@ServerEndpoint(value = "/channel/echo/{token}")
public class EchoChannel{
    // 日志记录器
    private static final Logger LOGGER = LoggerFactory.getLogger(EchoChannel.class);
    
    // JWT密钥和过期时间（从配置文件中获取）
    private static String secret = "123456abcdefghijklmnopqrstuvwxyz";
    private static int expireTime = 7;
    
    //创建安全线程集合
    private static final Set<Map<String, String>> SESSIONST = Collections.synchronizedSet(new HashSet<>());
    /**concurrent包的线程安全Set，用来存放每个客户端对应的WebSocket对象。*/
    private static final ConcurrentHashMap<String, String> webSocketMap = new ConcurrentHashMap<>();
    private static final Set<Session> SESSIONS = Collections.synchronizedSet(new HashSet<>());
    // 获取当前会话
    private Session session;
    
    // 用户ID
    private String userId;
    
    // 在线人数计数器
    private static final AtomicInteger onlineCount = new AtomicInteger(0);

    // 收消息
    @OnMessage
    public void onMessage(String message) throws IOException {
        String senderId = session.getId().toString();
        String timestamp = "[" + Instant.now().toEpochMilli() + "]";

        LOGGER.info("[websocket] 收到消息：id={}，message={}", this.session.getId(), message);

        if ("bye".equalsIgnoreCase(message)) {
            // 由服务器主动关闭连接。状态码为 NORMAL_CLOSURE（正常关闭）。
            this.session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Bye"));;
            return;
        }

        // 广播消息给所有客户端（包括自己，前端可忽略自己的消息）
        String broadcastMsg = timestamp + " " +
                (senderId.length() >= 8 ? senderId.substring(0, 8) : senderId)
                + ": " + message;
        broadcast(broadcastMsg);

    }
    
    // 连接打开
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) {
        this.session = session;
        SESSIONS.add(session);
        JwtUtils jwtUtils = new JwtUtils(secret, expireTime);
        Claims claims = jwtUtils.parseToken(token);
        this.userId = claims.get("id", String.class);
        webSocketMap.put("Id", this.userId);
        
        // 在线人数增加
        int currentOnlineCount = onlineCount.incrementAndGet();
        
        // 解析token获取用户ID
        try {

            LOGGER.info("[websocket] 用户 {} 连接打开：id={}", webSocketMap.get("Id"), session.getId());
            // 广播消息，包含当前在线人数
            broadcast("系统消息：用户 " + webSocketMap.get("Id") + " 加入了聊天室，当前在线人数：" + currentOnlineCount);
        } catch (BlogException e) {
            // 处理token验证失败的情况
            LOGGER.error("[websocket] token验证失败：{}", e.getMessage());
            // 广播错误消息
            broadcast("系统消息：用户连接验证失败，原因：" + e.getMessage());
            // 关闭连接
            try {
                session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "Token验证失败"));
            } catch (IOException ioException) {
                LOGGER.error("[websocket] 关闭连接失败", ioException);
            }
        } catch (Exception e) {
            LOGGER.error("[websocket] token解析失败：{}", e.getMessage());
            // 广播错误消息
            broadcast("系统消息：用户连接出现异常");
            // 关闭连接
            try {
                session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, "服务器内部错误"));
            } catch (IOException ioException) {
                LOGGER.error("[websocket] 关闭连接失败", ioException);
            }
        }
    }
    
    // 断开连接
    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        synchronized (SESSIONS) {
            SESSIONS.remove(session);
        }
        
        // 在线人数减少
        int currentOnlineCount = onlineCount.decrementAndGet();
        
        LOGGER.info("[websocket] 用户 {} 断开连接：id={}, closeReason={}", webSocketMap.get("Id"), session.getId(), closeReason);
        // 广播用户离开，包含当前在线人数
        broadcast("系统消息：用户 " + webSocketMap.get("Id") + " 离开了聊天室，当前在线人数：" + currentOnlineCount);
    }
    
    // 出错
    @OnError
    public void onError(Session session, Throwable throwable) throws IOException {
        LOGGER.error("[websocket] 用户 {} 出错：id={}", this.userId, session.getId(), throwable);
        // 关闭连接。状态码为 UNEXPECTED_CONDITION（意料之外的异常）
        this.session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, throwable.getMessage()));
        broadcast("系统消息：用户 " + this.userId + " 连接异常断开");
    }
    
    // 广播消息到所有客户端
    private void broadcast(String message) {
        for (Session session : SESSIONS) {
            try {
                if (session.isOpen()) {
                    session.getAsyncRemote().sendText(message);
                }
            } catch (Exception e) {
                LOGGER.error("广播失败", e);
                SESSIONS.remove(session); // 移除异常连接
            }
        }
    }
    
    // 获取用户ID
    public String getUserId() {
        return userId;
    }
    
    // 获取当前在线人数
    public static int getOnlineCount() {
        return onlineCount.get();
    }
    
    // 设置JWT密钥
    @Value("${token.secret:123456abcdefghijklmnopqrstuvwxyz}")
    public void setSecret(String secret) {
        EchoChannel.secret = secret;
    }
    
    // 设置过期时间
    @Value("${token.expireTime:7}")
    public void setExpireTime(int expireTime) {
        EchoChannel.expireTime = expireTime;
    }
}